package demo.generate.Demo_generateClass;

import java.io.FileOutputStream;
import java.lang.reflect.Method;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
 * Created by zhangyanfei on 2018/10/17.
 *
 * @author zhangyanfei
 * @date 2018/10/17
 */
public class InterfaceHandler extends ClassLoader implements Opcodes {
    public static Object makeClass(Class<?> clazz) throws Exception {
        String interfaceName = clazz.getSimpleName();
        String className = interfaceName + "$Impl";
        String innerClassName = Type.getInternalName(clazz);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        // 类名
        cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, className, null,
            "java/lang/Object", new String[] {innerClassName});
        //创建一个方法
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        //将变量 this 入操作数栈
        mv.visitVarInsn(ALOAD, 0);
        //调用super()
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mv.visitInsn(RETURN);
        // 栈桢和局部变量大小
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            makeMethod(cw, method.getName(), className);
        }
        //写入文件
        byte[] code = cw.toByteArray();
        FileOutputStream fos = new FileOutputStream(
            "/Users/zhangyanfei/workspace/hissy-common/src/test/java/demo/generate/Demo_generateClass/" + className
                + ".class");
        fos.write(code);
        fos.close();

        //从文件加载类
        InterfaceHandler loader = new InterfaceHandler();
        Class<?> exampleClass = loader.defineClass(className, code, 0,
            code.length);

        Object obj = exampleClass.getConstructor().newInstance();
        return obj;
    }

    private static void makeMethod(ClassWriter cw, String name, String className) {
        //访问权限、方法名、方法jvm描述、异常
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, name, "()V", null, null);
        //  成员变量入栈  操作指令 属性所属类名 属性名  类型描述
        mv.visitFieldInsn(GETSTATIC, Type.getInternalName(System.class), "out", "Ljava/io/PrintStream;");
        //  常量入栈
        mv.visitLdcInsn("调用方法[" + name + "]");
        //  方法入栈  操作指令、方法所属类名、方法描述
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
            "(Ljava/lang/String;)V");
        // 指令入栈
        mv.visitInsn(RETURN);
        //  设置栈桢大小 本地成员变量
        mv.visitMaxs(2, 1);
        mv.visitEnd();
    }
}
